package contrl

import (
	"core/env"
	"core/graylog"
	"core/ipip"
	"core/rds"
	"core/utils"
	"db"
	"errors"
	"fmt"
	"time"

	"core/mysql"

	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis"
	"github.com/medivhzhan/weapp"
)

type BasicCtl struct{}

func (*BasicCtl) HelloWorld(c *gin.Context) (res interface{}, e Error) {
	return nil, GenError(errors.New("test err"))
}

type BasicPlayer struct {
	Uid      int64
	Name     string
	HeadImg  string
	Gender   int
	Age      int
	Province string
	City     string
}

func genBasicPlayer(u *db.User) BasicPlayer {
	return BasicPlayer{
		Uid:      u.UID,
		Name:     u.Name,
		HeadImg:  u.HeadImg,
		Gender:   u.Gender,
		Age:      u.Age,
		Province: u.Province,
		City:     u.City,
	}
}

type InvitePlayer struct {
	BasicPlayer
	GotReward bool
	InviteAt  int64
}

func genInviteList(u *db.User) (userInfos []InvitePlayer) {
	userInfos = make([]InvitePlayer, 0)
	ids := make([]int64, 0)
	for _, invitee := range u.Invitees {
		ids = append(ids, invitee.UID)
	}
	if len(ids) == 0 {
		return
	}
	users := db.LoadByIds(ids)
	var count int
	for _, invitee := range u.Invitees {
		if count >= env.Config.InviteLimit {
			break
		}
		player, found := users[invitee.UID]
		if !found {
			continue
		}
		userInfos = append(userInfos, InvitePlayer{
			BasicPlayer: genBasicPlayer(player),
			GotReward:   invitee.GotReward,
			InviteAt:    invitee.InviteAtUnix,
		})
		count++
	}
	return
}

func genInvitor(u *db.User) (info BasicPlayer) {
	if u.Invitor == 0 {
		return
	}
	invitor, err := db.LoadByUID(u.Invitor, false)
	if err != nil {
		utils.Error(err)
		return
	}
	info = BasicPlayer{
		Uid:      invitor.UID,
		Name:     invitor.Name,
		HeadImg:  invitor.HeadImg,
		Gender:   invitor.Gender,
		Age:      invitor.Age,
		Province: invitor.Province,
		City:     invitor.City,
	}
	return
}

type BasicLevel struct {
	Level map[int]map[int]db.LevelDetail
}

func genLevels(u *db.User) (basicLevel BasicLevel) {
	basicLevel.Level = make(map[int]map[int]db.LevelDetail)
	for menuId, item := range u.Level {
		if menuId == db.CustomMenuID {
			continue
		}
		for level, detail := range item {
			if u.Loves[db.EncodeLove(menuId, level)] > 0 {
				detail.Love = true
			}
			if basicLevel.Level[menuId] == nil {
				basicLevel.Level[menuId] = map[int]db.LevelDetail{level: detail}
			} else {
				basicLevel.Level[menuId][level] = detail
			}
		}
	}

	return
}

type CustomLevel struct {
	CustomLevel map[int]db.LevelDetail
}

func genCustomLevels(u *db.User) (customLevel CustomLevel) {
	customLevel.CustomLevel = make(map[int]db.LevelDetail)
	for menuId, item := range u.Level {
		if menuId == db.CustomMenuID {
			for level, detail := range item {
				if u.Loves[db.EncodeLove(menuId, level)] > 0 {
					detail.Love = true
				}
				customLevel.CustomLevel[level] = detail
			}
		}
	}
	return
}

type UserBasicConf struct {
	FreeGrassCoolHour int
}

func genUserBasicConf() (conf UserBasicConf) {
	conf = UserBasicConf{
		FreeGrassCoolHour: env.Config.FreeGrassCoolHour,
	}
	return
}

type BasicInfo struct {
	Uid              int64
	Authorize        bool
	BasicPlayer      BasicPlayer
	Invitor          BasicPlayer
	OpenId           string
	CanShare         bool
	GoldNum          int `json:"goldNum"`
	GotFreeGrassAt   int64
	Water            int
	WatchVideoRemain int
	WatchVideoLimit  int
	ShareCode        string
	InviteList       []InvitePlayer
	BasicLevel
	CustomLevel
	UserBasicConf
	MenuConf   []env.MenuConf
	InviteConf []env.InviteConf
}

func genBasicInfo(c *gin.Context, u *db.User) (info BasicInfo) {
	return BasicInfo{
		Uid:              u.UID,
		Authorize:        u.Authorize,
		BasicPlayer:      genBasicPlayer(u),
		Invitor:          genInvitor(u),
		OpenId:           u.OpenID,
		CanShare:         ipip.CanShare(utils.GetIP(c)),
		GoldNum:          u.Grass,
		GotFreeGrassAt:   u.GotFreeGrassAt.Unix(),
		Water:            u.Water,
		WatchVideoRemain: env.Config.WatchVideoLimit - u.WatchVideoCount,
		WatchVideoLimit:  env.Config.WatchVideoLimit,
		ShareCode:        db.EncodeShareCode(u.UID),
		InviteList:       genInviteList(u),
		BasicLevel:       genLevels(u),
		CustomLevel:      genCustomLevels(u),
		UserBasicConf:    genUserBasicConf(),
		MenuConf:         env.MenuList,
		InviteConf:       env.InviteConfs,
	}
}

type LoginResponse struct {
	Token   string
	NewUser bool
	BasicInfo
}

func (*BasicCtl) Login(c *gin.Context) (res interface{}, e Error) {
	debug := c.GetString("debug")
	var weappRes weapp.LoginResponse
	var err error
	var expireHour time.Duration

	if debug != "" {
		if env.RunMode == env.RunModeProd && debug != "chimps" {
			return nil, GenErrorMsg("参数错误，code不能为空")
		}
		weappRes = weapp.LoginResponse{
			OpenID:     fmt.Sprintf("%s-openId", debug),
			SessionKey: fmt.Sprintf("%s-sessionId", debug),
			UnionID:    fmt.Sprintf("%s-unionId", debug),
		}
		expireHour = 24 * 30
	} else {
		code := c.GetString("code")
		if code == "" {
			return nil, GenErrorMsg("参数错误，code不能为空")
		}
		weappRes, err = weapp.Login(env.Conf.AppId, env.Conf.AppSecret, code)
		if err != nil {
			return nil, GenError(err)
		}
		expireHour = 24 * 7
	}

	uid, err := db.LoadUIDByOpenId(weappRes.OpenID)
	var u *db.User
	var newUser bool
	if err == redis.Nil || err == mysql.NotFound {
		invitorUID := db.DecodeShareCode(c.GetString("share_code"))
		preUser := new(db.User)
		if debug != "" {
			preUser.Grass = 50
		}
		u, err = db.Create(weappRes.OpenID, weappRes.SessionKey, weappRes.UnionID, invitorUID, preUser)
		if err != nil {
			return nil, GenError(err)
		}
		newUser = true
		go graylog.Info("新用户注册", graylog.M{
			"playerId":   u.UID,
			"openId":     u.OpenID,
			"shareCode":  c.GetString("share_code"),
			"request":    fmt.Sprintf("%+v", c.Request.Form),
			"invitorUID": invitorUID,
			"clientIp":   utils.GetIP(c),
		})
	} else if err != nil {
		return nil, GenError(err)
	} else {
		u, err = db.LoadByUID(uid, true)
		if err != nil {
			return nil, GenError(err)
		}
	}

	go graylog.Info("登录", graylog.M{
		"playerId":       u.UID,
		"name":           u.Name,
		"openId":         u.OpenID,
		"unionId":        u.UnionId,
		"gotFreeGrassAt": u.GotFreeGrassAt,
		"finishLevelAt":  u.FinishLevelAt,
		"grass":          u.Grass,
		"levels":         fmt.Sprintf("%+v", u.Level),
	})

	res = LoginResponse{
		Token:     utils.Encrypt(Sign{Uid: u.UID, ExpiredAt: time.Now().Add(time.Hour * expireHour)}, env.Conf.AppSecret2),
		NewUser:   newUser,
		BasicInfo: genBasicInfo(c, u),
	}
	return
}

func (*BasicCtl) BasicInfo(c *gin.Context) (res interface{}, e Error) {
	sign := GetSign(c)
	u, err := db.LoadByUID(sign.Uid, true)
	if err != nil {
		return nil, GenError(err)
	}
	//openId, err := db.LoadOpenIdByUID(sign.Uid)
	//if openId == "" {
	//	utils.Error(openId, err)
	//	return nil, GenErrorCode(40001, "账户已被删除")
	//}

	go graylog.Info("缓存登录", graylog.M{
		"playerId":       u.UID,
		"name":           u.Name,
		"gotFreeGrassAt": u.GotFreeGrassAt,
		"grass":          u.Grass,
		"levels":         fmt.Sprintf("%+v", u.Level),
	})
	res = LoginResponse{
		BasicInfo: genBasicInfo(c, u),
		Token:     "",
	}
	return
}

func (*BasicCtl) Aof(c *gin.Context) (res interface{}, e Error) {
	aofKey := db.MakeAofKey(time.Now().AddDate(0, 0, -1))
	count := rds.GetDefaultClient().SCard(aofKey).Val()
	go db.RunAof(aofKey)
	go graylog.Info("aof落地", graylog.M{
		"count": count,
	})
	res = count
	return
}

func (*BasicCtl) AofToday(c *gin.Context) (res interface{}, e Error) {
	aofKey := db.MakeAofKey(time.Now())
	count := rds.GetDefaultClient().SCard(aofKey).Val()
	go db.RunAof(aofKey)
	go graylog.Info("aof落地", graylog.M{
		"count": count,
	})
	res = count
	return
}

type BasicConfResponse struct {
	ServerUrl    string
	RunMode      string
	DevVersion   string
	AuditVersion string
}

func (*BasicCtl) GetConf(c *gin.Context) (res interface{}, e Error) {
	clientVersion := c.GetString("version")
	devVersion, auditVersion := db.LoadVersion()

	var runMode string
	switch clientVersion {
	case devVersion:
		runMode = env.RunModeDev
	case auditVersion:
		runMode = env.RunModeAudit
	default:
		runMode = env.RunModeProd
	}

	res = BasicConfResponse{
		ServerUrl:    env.Confs[runMode].ServerUrl,
		RunMode:      runMode,
		DevVersion:   devVersion,
		AuditVersion: auditVersion,
	}
	return
}
